//
// Copyright (c) 2009 All Right Reserved
//
// vl
//
// 2009-01-01
// Contains ...
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using JetBrains.Annotations;
namespace LargoCommon.Music
{
///
/// Harmonic space to help analyze of harmony.
///
public sealed class HarmonicSpace {
#region Fields
///
/// Near-zero value.
///
private const float NearZero = 0.01f;
///
/// Harmonic System.
///
private readonly HarmonicSystem harmonicSystem;
///
/// Musical bands.
///
private List musicalBands;
#endregion
#region Constructors
///
/// Initializes a new instance of the HarmonicSpace class.
///
/// The given harmonic system.
public HarmonicSpace(HarmonicSystem givenHarmonicSystem) {
Contract.Requires(givenHarmonicSystem != null);
this.harmonicSystem = givenHarmonicSystem;
this.MusicalBands = new List();
for (var i = 0; i < this.harmonicSystem.Order; i++) {
var b = new HarmonicBand(i, 0.0f);
this.MusicalBands.Add(b);
}
}
///
/// Initializes a new instance of the class.
///
[UsedImplicitly]
public HarmonicSpace() {
}
#endregion
#region Public Properties
///
/// Gets or sets a value indicating whether [consider energy decrease in time].
///
///
/// True if [consider energy decrease in time]; otherwise, false.
///
[UsedImplicitly]
public bool ConsiderEnergyDecreaseInTime { get; set; } //// CA1044 (FxCop)
///
/// Gets or sets a value indicating whether [consider harmonic bindings].
///
///
/// True if [consider harmonic bindings]; otherwise, false.
///
[UsedImplicitly]
public bool ConsiderImpulseBindings { get; set; } //// CA1044 (FxCop)
///
/// Gets or sets a value indicating whether [strong impulse bindings].
///
///
/// True if [strong impulse bindings]; otherwise, false.
///
[UsedImplicitly]
public bool StrongImpulseBindings { get; set; } //// CA1044 (FxCop)
///
/// Gets or sets a value indicating whether [consider continuity bindings].
///
///
/// True if [consider continuity bindings]; otherwise, false.
///
[UsedImplicitly]
public bool ConsiderContinuityBindings { get; set; } //// CA1044 (FxCop)
#endregion
#region Private Properties
///
/// Gets or sets the musical bands.
///
///
/// The musical bands.
///
private IList MusicalBands {
get {
Contract.Ensures(Contract.Result>() != null);
return this.musicalBands ?? (this.musicalBands = new List());
}
set => this.musicalBands = (List)value;
}
#endregion
#region Public methods
///
/// Reset analyzer.
///
/// Harmonic modality.
public void Reset(BinaryStructure harmonicModality) { //// HarmonicModality
Contract.Requires(harmonicModality != null);
for (byte i = 0; i < this.harmonicSystem.Order; i++) {
if (i >= this.MusicalBands.Count) {
continue;
}
if (this.MusicalBands[i] == null) {
continue;
}
this.MusicalBands[i].Value = 0.0f;
this.MusicalBands[i].Modal = harmonicModality.IsOn(i);
this.MusicalBands[i].SonanceBonus = 0.0f;
}
}
///
/// Forgets this instance.
///
public void Forget() {
const int forgetFactor = 2;
for (byte i = 0; i < this.harmonicSystem.Order; i++) {
if (i >= this.MusicalBands.Count) {
continue;
}
if (this.MusicalBands[i] == null) {
continue;
}
this.MusicalBands[i].Value = this.MusicalBands[i].Value / forgetFactor;
}
}
///
/// Melodic tone collection.
///
/// Melodic tones.
public void AcceptTonesOfTheTick(IEnumerable tones) {
const float withdraw = 0.5f; //// 0.1f seems to be not enough
Contract.Requires(tones != null);
//// if (tones == null) { return; }
if (this.ConsiderEnergyDecreaseInTime) {
//// Stepwise decrease of band activity
foreach (var mb in this.MusicalBands) {
if (mb.Value > 0) {
mb.Value = Math.Max(mb.Value - withdraw, 0.0f);
}
if (mb.Value < 0) {
mb.Value = Math.Min(mb.Value + withdraw, 0.0f);
}
}
}
var elements = (from MusicalTone tone in tones
where tone != null && tone.IsTrueTone
select tone.Pitch.SystemAltitude % this.harmonicSystem.Order).ToList(); ////.Distinct();
elements.ForEach(band => this.ChangeToneValue(band, 1.0f));
//// soundingTones.I
}
///
/// Determine harmonic structure.
///
/// The given max tones in chord.
/// If set to true [full harmonization].
///
/// Returns value.
///
public HarmonicStructure DetermineHarmonicStructure(byte givenMaxTonesInChord, bool fullHarmonization) {
var hstruct = new HarmonicStructure(this.harmonicSystem, (string)null);
int limit = givenMaxTonesInChord; //// max number of tones in the structure
if (fullHarmonization) {
const float minimumValue = 10.0f; //// 0.1
const float bandLowerLimit = -1000.0f;
this.ResetSonanceBonus();
//// Sorts toneValues by weight
var positiveBands = (from band in this.MusicalBands
where band.Modal && band.Value > minimumValue
orderby band.Value
descending
select (byte)band.Number).ToList();
if (positiveBands.Count < limit) {
limit = positiveBands.Count;
}
for (var number = 0; number < limit; number++) {
//// Sorts toneValues by weight
var bands = (from band in this.MusicalBands
where band.Modal && band.Value + band.SonanceBonus > minimumValue
orderby band.Value + band.SonanceBonus
descending
select (byte)band.Number).ToList();
byte elem;
if (bands.Count > 0) {
elem = bands.FirstOrDefault(); ////0.01
}
else {
var topBands = (from band in this.MusicalBands
orderby band.Value + band.SonanceBonus
descending
select band).ToList();
//// For four-part harmonization are full chords needed, so no bandLowerLimit can be used;
elem = (from band in topBands
where band.Modal && band.Value + band.SonanceBonus > bandLowerLimit
select (byte)band.Number).FirstOrDefault(); ////0.01
}
hstruct.On(elem);
this.IncreaseSonanceBonus(elem, 1.0f);
}
}
else {
const float bandLowerLimit = 0.1f;
//// Sorts toneValues by weight
var topBands = (from mb in this.MusicalBands orderby mb.Value descending select mb).ToList();
foreach (var elem in from band in topBands
where band.Modal && band.Value > bandLowerLimit
select (byte)band.Number) { ////0.01
hstruct.On(elem);
limit--;
if (limit == 0) {
break;
}
}
}
hstruct.DetermineLevel();
hstruct.DetermineBehavior();
//// if (hstruct.FormalImpulse > 24) { return lastHarmonicStructure; }
hstruct.DetermineStructuralCode();
hstruct.DetermineShortcut();
//// string s = hstruct.ToneSchema;
return hstruct;
}
#endregion
#region Private methods
///
/// Resets the consonance bonus.
///
private void ResetSonanceBonus() {
for (byte i = 0; i < this.harmonicSystem.Order; i++) {
if (i >= this.MusicalBands.Count) {
continue;
}
if (this.MusicalBands[i] == null) {
continue;
}
this.MusicalBands[i].SonanceBonus = 0.0f;
}
}
///
/// Increases the consonance bonus.
///
/// The band number.
/// The value.
private void IncreaseSonanceBonus(int bandNumber, float value) {
Contract.Requires(this.harmonicSystem != null);
int order = this.harmonicSystem.Order;
//// Exclude used band and its halftone surrounding (2015/1)
if (this.MusicalBands[bandNumber] != null) {
var bimpulse1d = (bandNumber + order - 1) % order;
var bimpulse1U = (bandNumber + 1) % order;
this.MusicalBands[bandNumber].SonanceBonus -= 1000;
this.MusicalBands[bimpulse1d].SonanceBonus -= 100;
this.MusicalBands[bimpulse1U].SonanceBonus -= 100;
}
//// float halfValue = value/2;
var bcontinuity7U = (bandNumber + order + 7) % order;
var bcontinuity4U = (bandNumber + order + 4) % order;
var bcontinuity5U = (bandNumber + order - 7) % order;
var bcontinuity8U = (bandNumber + order - 4) % order;
//// int bcontinuity3u = (bandNumber + order + 3) % order;
//// int bcontinuity9u = (bandNumber + order - 3) % order;
if (this.MusicalBands[bcontinuity7U] != null) {
this.MusicalBands[bcontinuity7U].SonanceBonus += value;
}
if (this.MusicalBands[bcontinuity4U] != null) {
this.MusicalBands[bcontinuity4U].SonanceBonus += value;
}
if (this.MusicalBands[bcontinuity5U] != null) {
this.MusicalBands[bcontinuity5U].SonanceBonus += value;
}
if (this.MusicalBands[bcontinuity8U] != null) {
this.MusicalBands[bcontinuity8U].SonanceBonus += value;
}
}
///
/// For 12-tone system only.
///
/// The band number.
/// Energy value.
private void ChangeToneValue(int bandNumber, float value) {
Contract.Requires(bandNumber >= 0);
byte primaryInfluenceFactor = 3; //// 2
byte secondaryInfluenceFactor = 6; //// 4
if (this.StrongImpulseBindings)
{
primaryInfluenceFactor = 1; //// 2
secondaryInfluenceFactor = 2; //// 4
}
var quotientV1 = value / primaryInfluenceFactor;
var quotientV2 = value / secondaryInfluenceFactor;
var harSystem = this.harmonicSystem;
if (harSystem.Order == 0) {
return;
}
if (bandNumber >= 0 && bandNumber < this.MusicalBands.Count && this.MusicalBands[bandNumber] != null) {
this.MusicalBands[bandNumber].Value += value;
//// if (impulse1) { this.MusicalBands[bandNumber].Value -= quotientV1; }
}
if (this.ConsiderImpulseBindings) {
//// Main influence
this.MainInfluence(bandNumber, value, quotientV1);
}
if (this.ConsiderContinuityBindings) {
//// Continuity to central tone
this.ContinuityToCentralTone(bandNumber, quotientV1, quotientV2);
}
//// Influence due to sound color
this.InfluenceDueToSoundColor(bandNumber, value);
}
///
/// Mains the influence.
///
/// The band number.
/// The value.
/// The quotient v1.
private void MainInfluence(int bandNumber, float value, float quotientV1) {
Contract.Requires(this.harmonicSystem != null);
int order = this.harmonicSystem.Order;
var halfValue = value / 2;
var bimpulse2d = (bandNumber + order - 2) % order;
var bimpulse1d = (bandNumber + order - 1) % order;
var bimpulse1U = (bandNumber + 1) % order;
var bimpulse2U = (bandNumber + 2) % order;
//// bool impulse1 = false; bool impulse2 = false;
if (this.MusicalBands[bimpulse2d] != null) {
//// if (this.MusicalBands[bimpulse2d].Value > 0) {
this.MusicalBands[bimpulse2d].Value = Math.Max(this.MusicalBands[bimpulse2d].Value - quotientV1, NearZero);
//// }
////impulse2 = true;
}
if (this.MusicalBands[bimpulse2U] != null) {
//// 2013/08 this.MusicalBands[bimpulse2u].Value = -quotientV1;
//// if (this.MusicalBands[bimpulse2u].Value > 0) {
this.MusicalBands[bimpulse2U].Value = Math.Max(this.MusicalBands[bimpulse2U].Value - quotientV1, NearZero);
//// }
////impulse2 = true;
}
if (this.MusicalBands[bimpulse1d] != null) {
this.MusicalBands[bimpulse1d].Value -= halfValue; //// -= quotientV1;
////impulse1 = true;
}
if (this.MusicalBands[bimpulse1U] != null) {
this.MusicalBands[bimpulse1U].Value -= halfValue; ////-= quotientV1;
////impulse1 = true;
}
}
///
/// Influences the color of the due to sound.
///
/// The band number.
/// The value.
private void InfluenceDueToSoundColor(int bandNumber, float value) {
Contract.Requires(this.harmonicSystem != null);
int order = this.harmonicSystem.Order;
var colorValue = value / 10;
var bcontinuity7U = (bandNumber + order + 7) % order;
var bcontinuity4U = (bandNumber + order + 4) % order;
if (this.MusicalBands[bcontinuity7U] != null) {
this.MusicalBands[bcontinuity7U].Value += colorValue;
}
if (this.MusicalBands[bcontinuity4U] != null) {
this.MusicalBands[bcontinuity4U].Value += colorValue;
}
//// toneValue[bcontinuity4u] -= v2;
//// toneValue[bcontinuity7u] -= v4;
}
///
/// Continuities to central tone.
///
/// The band number.
/// The quotient v1.
/// The quotient v2.
private void ContinuityToCentralTone(int bandNumber, float quotientV1, float quotientV2) {
Contract.Requires(this.harmonicSystem != null);
int order = this.harmonicSystem.Order;
var bcontinuity7d = (bandNumber + order - 7) % order;
var bcontinuity4d = (bandNumber + order - 4) % order;
//// int bcontinuity4u = (band + 4) % harSystem.Order;
//// int bcontinuity7u = (band + 7) % harSystem.Order;
if (this.MusicalBands[bcontinuity7d] != null) {
if (this.MusicalBands[bcontinuity7d].Value < 0) {
this.MusicalBands[bcontinuity7d].Value = Math.Min(this.MusicalBands[bcontinuity7d].Value + quotientV1, -NearZero);
}
}
if (this.MusicalBands[bcontinuity4d] == null) {
return;
}
if (this.MusicalBands[bcontinuity4d].Value < 0) {
this.MusicalBands[bcontinuity4d].Value = Math.Min(this.MusicalBands[bcontinuity4d].Value + quotientV2, -NearZero);
}
}
#endregion
}
}